package com.xinan.tool.aoi.service.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinan.tool.aoi.entity.AoiBaiDuData;
import com.xinan.tool.aoi.entity.AoiData;
import com.xinan.tool.aoi.entity.AoiTableConfig;
import com.xinan.tool.aoi.mapper.AoiDealMapper;
import com.xinan.tool.aoi.service.IAoiDealService;
import com.xinan.tool.aoi.util.BaiduMokatorToLngLat;
import com.xinan.tool.aoi.util.CoordinateTransferUtils;
import com.xinan.tool.common.QueryParameter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author yuanzheng
 * @since 2025-08-26
 */
@Slf4j
@Service
public class AoiDealServiceImpl extends ServiceImpl<AoiDealMapper, AoiData> implements IAoiDealService {

    @Resource
    private AoiDealMapper aoiDealMapper;

    @Override
    public String transGcj02ToWgs84(AoiTableConfig aoiTableConfig) {
        // 分页查询参数
        String tableName = aoiTableConfig.getSource_table();
        int curPage = 1, pageSize = 150;
        QueryParameter queryParameter = new QueryParameter();
        queryParameter.setTableName(tableName);
        queryParameter.setCurPage(curPage);
        queryParameter.setPageSize(pageSize);
        queryParameter.setSortName("ogc_fid");
        // 处理逻辑
        long start = System.currentTimeMillis();
        List<AoiData> data;
        int pageSizeQuery;
        while (true) {
            System.out.println(String.format("当前查询第[ %s ]页的[ %s ]条数据", curPage, pageSize));
            long sc1 = System.currentTimeMillis();
            data = aoiDealMapper.queryAoiDataList(queryParameter);
            long startIn = System.currentTimeMillis();
            log.info("transGcj02ToWgs84 list size:[{}}.", data.size());
            data.forEach(aoiData -> {
                // 处理边界数据
                String wkbGeometry = aoiData.getWkbGeometry();
                aoiData.setWkbGeometry(dealGeometry(wkbGeometry));
                // 点数据1
                BigDecimal navigeomet = aoiData.getNavigeomet();
                BigDecimal navigeom1 = aoiData.getNavigeom1();
                Map<String, Double> pointOne = dealPoint(navigeomet, navigeom1);
                aoiData.setNavigeomet(BigDecimal.valueOf(MapUtil.getDouble(pointOne, "lon")));
                aoiData.setNavigeom1(BigDecimal.valueOf(MapUtil.getDouble(pointOne, "lat")));
                // 点数据2
                BigDecimal x = aoiData.getX();
                BigDecimal y = aoiData.getY();
                Map<String, Double> pointTwo = dealPoint(x, y);
                aoiData.setX(BigDecimal.valueOf(MapUtil.getDouble(pointTwo, "lon")));
                aoiData.setY(BigDecimal.valueOf(MapUtil.getDouble(pointTwo, "lat")));
                // 数据点3
                BigDecimal centerx = aoiData.getCenterx();
                BigDecimal centery = aoiData.getCentery();
                Map<String, Double> pointCenter = dealPoint(centerx, centery);
                aoiData.setCenterx(BigDecimal.valueOf(MapUtil.getDouble(pointCenter, "lon")));
                aoiData.setCentery(BigDecimal.valueOf(MapUtil.getDouble(pointCenter, "lat")));
            });
            // 分批入库
            int numberBatch = 32767;
            double number = data.size() * 47.0 / numberBatch;
            int n = ((Double) Math.ceil(number)).intValue();
            for (int i = 0; i < n; i++) {
                int end = numberBatch * (i + 1);
                if (end > data.size()) {
                    end = data.size();
                }
                List<AoiData> insertList = data.subList(numberBatch * i, end);
                aoiDealMapper.saveAoiDataList(insertList, aoiTableConfig.getTarget_table());
            }
            log.info("transGcj02ToWgs84 deal cost:[{}]ms.", System.currentTimeMillis() - startIn);
            long sc2 = System.currentTimeMillis();
            System.out.println(String.format("当前第[ %s ]页的[ %s ]条数据标准化耗时[ %s ]毫秒", curPage, pageSize, sc2 - sc1));
            // 跳出循环
            int querySize = data.size();
            pageSizeQuery = querySize;
            if (querySize < pageSize) {
                break;
            }
            // 增加当前页
            queryParameter.setCurPage(++curPage);
        }
        long end = System.currentTimeMillis();
        long use = end - start;
        return "GP数据库查询:" + tableName + "-" + (pageSize * (curPage - 1) + pageSizeQuery) + "条数据总耗时：" + use + "毫秒。";
    }

    @Override
    public String getAoiByName(AoiTableConfig aoiTableConfig) {
        // 分页查询参数
        String sourceTable = aoiTableConfig.getSource_table();
        int curPage = 1, pageSize = 150;
        QueryParameter queryParameter = new QueryParameter();
        queryParameter.setTableName(sourceTable);
        queryParameter.setCurPage(curPage);
        queryParameter.setPageSize(pageSize);
        queryParameter.setSortName("ogc_fid");
        // 处理逻辑
        long start = System.currentTimeMillis();
        List<AoiBaiDuData> data;
        int pageSizeQuery;
        while (true) {
            System.out.println(String.format("当前查询第[ %s ]页的[ %s ]条数据", curPage, pageSize));
            long sc1 = System.currentTimeMillis();
            data = aoiDealMapper.queryBaiDuAoiDataList(queryParameter);
            long startIn = System.currentTimeMillis();
            log.info("getAoiByName list size:[{}}.", data.size());
            data.forEach(aoiBaiDuData -> {
                String name = aoiBaiDuData.getName();
                try {
                    String aoi = getBaiDuAoi("南京市鼓楼区", name, true);
                    if (StringUtils.isNotBlank(aoi)) {
                        aoiBaiDuData.setWkbGeometry(aoi);
                        aoiBaiDuData.setTableName(sourceTable);
                        aoiDealMapper.updateGeometryById(aoiBaiDuData);
                    } else {
                        log.error("getAoiByName name:[{}} Aoi is null.", name);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("getAoiByName Failed!");
                }
            });

            log.info("getAoiByName deal cost:[{}]ms.", System.currentTimeMillis() - startIn);
            long sc2 = System.currentTimeMillis();
            System.out.println(String.format("当前第[ %s ]页的[ %s ]条数据标准化耗时[ %s ]毫秒", curPage, pageSize, sc2 - sc1));
            // 跳出循环
            int querySize = data.size();
            pageSizeQuery = querySize;
            if (querySize < pageSize) {
                break;
            }
            // 增加当前页
            queryParameter.setCurPage(++curPage);
        }
        long end = System.currentTimeMillis();
        long use = end - start;
        return "GP数据库查询:" + sourceTable + "-" + (pageSize * (curPage - 1) + pageSizeQuery) + "条数据总耗时：" + use + "毫秒。";
    }

    @Override
    public String getBaiDuAoi(String region, String addressStr, boolean useKey) throws IOException {
        // ************* 以下是爬虫代码 *************
        if (useKey) {
            // 通过申请的 API_KEY 获取小区的UID 2000次/天
            String address = URLEncoder.encode(addressStr, String.valueOf(StandardCharsets.UTF_8));
            String searchUrl = "http://api.map.baidu.com/place/v2/search?query=" + address
                    + "&region=" + URLEncoder.encode(region, String.valueOf(StandardCharsets.UTF_8))
                    + "&output=json&ak=DQLlz9KmuiRSu6P1mtUuczVHwSOfr7fM";
            String searchRes = queryDataByUrlAndKey(searchUrl);
            List searchList = (List) JSONUtil.getByPath(JSONUtil.parseObj(searchRes), "results");
            if (CollectionUtils.isNotEmpty(searchList)) {
                Object uid = ((JSONObject) searchList.get(0)).get("uid");
                log.info("getBaiDuAoi uid:[{}]", uid);
                String geoUrl = "https://map.baidu.com/?reqflag=pcmap&from=webmap&qt=ext&uid=" + uid + "&ext_ver=new&l=18";
                String geoRes = queryDataByUrl(geoUrl);
                Map geoMap = (Map) JSONUtil.getByPath(JSONUtil.parseObj(geoRes), "content");
                String geo = MapUtil.getStr(geoMap, "geo");
                log.info("getBaiDuAoi geo:[{}]", geo);
                if (StringUtils.isNotBlank(geo)) {
                    String geometryStr = BaiduMokatorToLngLat.getGeometryByStr(geo);
                    log.info("getBaiDuAoi geometryStr:[{}]", geometryStr);
                    return geometryStr;
                }

            }
        } else {
            // 以下暂时无限制
            String address = URLEncoder.encode(region + addressStr, String.valueOf(StandardCharsets.UTF_8));
            String searchUrl = "http://map.baidu.com/su?wd=" + address + "&cid=347&type=0&pc_ver=2";
            String searchRes = queryDataByUrl(searchUrl);
            List searchList = (List) JSONUtil.getByPath(JSONUtil.parseObj(searchRes), "s");
            if (CollectionUtils.isNotEmpty(searchList)) {
                String best = searchList.get(0).toString();
                String[] bestArr = best.split("\\$");
                int length = bestArr.length;
                if (length > 0) {
                    String uid = bestArr[length - 1];
                    log.info("getBaiDuAoi uid:[{}]", uid);
                    String geoUrl = "https://map.baidu.com/?reqflag=pcmap&from=webmap&qt=ext&uid=" + uid + "&ext_ver=new&l=18";
                    String geoRes = queryDataByUrl(geoUrl);
                    Map geoMap = (Map) JSONUtil.getByPath(JSONUtil.parseObj(geoRes), "content");
                    String geo = MapUtil.getStr(geoMap, "geo");
                    log.info("getBaiDuAoi geo:[{}]", geo);
                    if (StringUtils.isNotBlank(geo)) {
                        String geometryStr = BaiduMokatorToLngLat.getGeometryByStr(geo);
                        log.info("getBaiDuAoi geometryStr:[{}]", geometryStr);
                        return geometryStr;
                    }
                }
            }
        }
        // 保存最新数据
        return "";
    }

    private String queryDataByUrl(String url) {
        int codeSuccess = 200;
        try {
            OkHttpClient client = new OkHttpClient().newBuilder().build();
            Request request = new Request.Builder()
                    .url(url)
                    .method("GET", null)
                    .addHeader("User-Agent", "Apifox/1.0.0 (https://apifox.com)")
                    .addHeader("Accept", "*/*")
                    .addHeader("Host", "map.baidu.com")
                    .addHeader("Connection", "keep-alive")
                    .addHeader("Cookie", "BAIDUID=CFBA4A90A9899DDD8AD715BC92ED774F:FG=1")
                    .build();
            Response responseSearch = client.newCall(request).execute();
            if (responseSearch != null && responseSearch.code() == codeSuccess) {
                return responseSearch.body().string();
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("queryDataByUrl Failed!");
        }
        return "";
    }

    private String queryDataByUrlAndKey(String url) {
        int codeSuccess = 200;
        try {
            OkHttpClient client = new OkHttpClient().newBuilder().build();
            Request request = new Request.Builder()
                    .url(url)
                    .method("GET", null)
                    .addHeader("User-Agent", "Apifox/1.0.0 (https://apifox.com)")
                    .addHeader("Accept", "*/*")
                    .addHeader("Host", "api.map.baidu.com")
                    .addHeader("Connection", "keep-alive")
                    .build();
            Response responseSearch = client.newCall(request).execute();
            if (responseSearch != null && responseSearch.code() == codeSuccess) {
                return responseSearch.body().string();
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("queryDataByUrlAndKey Failed!");
        }
        return "";
    }

    private String dealGeometry(String wkbGeometry) {
        String prefix = "MULTIPOLYGON(((";
        String postfix = ")))";
        String joinStr = ",";
        String[] pointArr = wkbGeometry.replace(prefix, "").replace(postfix, "").split(joinStr);
        List<String> pointStrList = new ArrayList<>();
        for (String point : pointArr) {
            String[] sourcePoint = point.split(" ");
            Map<String, Double> wgs84 = CoordinateTransferUtils.gcj02ToWgs84(Double.valueOf(sourcePoint[0]), Double.valueOf(sourcePoint[1]));
            String lon = MapUtil.getStr(wgs84, "lon");
            String lat = MapUtil.getStr(wgs84, "lat");
            pointStrList.add(lon + " " + lat);
        }
        return pointStrList.stream().collect(Collectors.joining(joinStr, prefix, postfix));
    }

    private Map<String, Double> dealPoint(BigDecimal lon, BigDecimal lat) {
        return CoordinateTransferUtils.gcj02ToWgs84(Double.valueOf(lon + ""), Double.valueOf(lat + ""));
    }
}
